//变量声明
// 1.var声明
// var a = 10;
// function f() {
//   var a = 10;
//   return function g() {
//     var b = a + 1;
//     return b;
//   };
// }
// var g = f(); //g=function g() {var b = a + 1; return b;}
// g(); //11
//
// function f() {
//   var a = 1;
//   a = 2;
//   var b = g();
//   a = 3;
//   return b;
//   function g() {
//     return a;
//   }
// }
// f(); // returns 2
// 作用域规则
// function f(shouldInitialize: boolean) {
//   if (shouldInitialize) {
//     var x = 10;
//   }
//   return x;
// }
// f(true); //10
// f(false); //undefined
// 捕获变量怪异之处
// for (var i = 0; i < 10; i++) {
//   setTimeout(function () {
//     console.log(i);
//   }, 100 * i); //结果10 10 10 10 10 10 10 10 10 10
// }
// for (var i = 0; i < 10; i++) {
//   //捕获“i”的当前状态
//   //通过用其当前值调用函数
//   (function (i) {
//     setTimeout(function () {
//       console.log(i);
//     }, 100 * i);
//   })(i);最后的这个(i)不写的话，不会输出
// }
// 2.let声明
// let hello = 'hello world';
// 块作用域
// function f(input: boolean) {
//   let a = 100;
//   if (input) {
//     let b = a + 1;
//     return b;
//   }
//   return b;// Error: 'b' doesn't exist here
// }
// 重定义及屏蔽
// 我们提过使用var声明时，它不在乎你声明多少次；你只会得到1个
// function f(x) {
//   var x;
//   var x;

//   if (true) {
//     var x;
//   }
// }
// let x = 10;
// let x = 20; // 错误，不能在1个作用域里多次声明`x`
// 块级作用域变量的获取
// function theCityThatAlwaysSleeps() {
//   let getCity;

//   if (true) {
//     let city = 'Seattle';
//     getCity = function () {
//       return city;
//     };
//   }

//   return getCity();
// }
// let a = theCityThatAlwaysSleeps();
// console.log(a);
// 不存在捕获变量怪异之处
// 注：当let声明出现在循环体里时拥有完全不同的行为。 不仅是在循环里引入了一个新的变量环境，而是针对 每次迭代都会创建这样一个新作用域。 这就是我们在使用立即执行的函数表达式时做的事，所以在 setTimeout例子里我们仅使用let声明就可以了。
// for (let i = 0; i < 10; i++) {
//   setTimeout(function () {
//     console.log(i); // 0 1 2 3 4 5 6 7 8 9
//   }, 100 * i);
// }
// 3.const 声明
// const numLivesForCat = 9;
//它们拥有与 let相同的作用域规则，但是不能对它们重新赋值。
// const numLivesForCat = 9;
// const kitty = {
//   name: 'Aurora',
//   numLives: numLivesForCat
// };
// 错误提示：Assignment to constant variable.（分配给常变量）。在ts文件中提示：无法分配到 "kitty" ，因为它是常数。
// kitty = {
//   name: 'Danielle',
//   numLives: numLivesForCat
// };
// // all "okay"
// console.log(kitty.name); //Aurora
// kitty.name = 'Rory';
// console.log(kitty.name); //Rory
// kitty.name = 'Kitty';
// // console.log(kitty.name);/Kitty
// kitty.name = 'Cat';
// kitty.numLives--;
// console.log(kitty.name, kitty.numLives); //'Cat', 8
// 4.let vs const
// 两种作用域相似的声明方式，到底应该使用哪个？答：使用最小特权原则，所有变量除了你计划去修改的都应该使用const。 基本原则就是如果一个变量不需要对它写入，那么其它使用这些代码的人也不能够写入它们，并且要思考为什么会需要对这些变量重新赋值。 使用 const也可以让我们更容易的推测数据的流动。
// 5.解构
// 5.1 解构数组;
// let input = [1, 2];
// let [first, second] = input; //等于 let first = input[0]; let secound = input[1];
// console.log(first, second); //1, 2
// [first, second] = [second, first];// swap variables 交换变量
// console.log(first, second); //2, 1
// 作用于函数参数：
// let input: [any, any] = [1, 2];
// function f([first, second]: [number, number]) {
//   console.log(first);
//   console.log(second);
// }
// f(input);
// let [first, ...rest] = [1, 2, 3, 4];
// console.log(first, rest); // 1, [ 2, 3, 4 ]
// 5.2 对象解构
// let o = {
//   a: 'foo',
//   b: 12,
//   c: 'car'
// };
// let { a, b } = o;
// console.log(a, b); //'foo', 12
// 属性重命名
// let o1 = {
//   a: 'foo',
//   b: 12,
//   c: 'car'
// };
// let { a, b }: { a: string; b: number } = o1;
// 默认值 // 这里的 ？表示这个参数 b 是一个可选参数
// function keepWholeObject(wholeObject: { a: string; b?: number }) {
//   let { a, b = 1001 } = wholeObject;
//   console.log(a, b); //调用这个方法，keepWholeObject(1) 结果为：undefind，1001 如果是keepWholeObject({a: 'foo',}); 结果为 'foo', 1001
// }

// export default keepWholeObject;
// 5.3 函数声明
// type c = { a: string; b?: number };
// function f({ a, b }: c): void {
//   console.log(1);
// } //这种情况下调用f()编辑器将会报错，提示：未提供与此绑定模式匹配的自变量。
// 通常会指定默认值
// function f1({ a = '', b = 0 } = {}): void {
//   console.log(1);
// }
// f1(); //这种情况下调用f1(),不会报错
// function f({ a, b = 0 } = { a: '' }): void {
//   console.log(1);
// }
// f({ a: 'yes' }); // ok, default b = 0
// f(); // ok, default to {a: ""}, which then defaults b = 0
// f({}); // 编辑器提示：类型“{}”的参数不能赋给类型“{ a: string; b?: number; }”的参数。类型 "{}" 中缺少属性 "a"，但类型 "{ a: string; b?: number; }" 中需要该属性。
// 5.4 展开 :展开操作符正与解构相反。 它允许你将一个数组展开为另一个数组，或将一个对象展开为另一个对象
// 5.4.1 展开数组
// let first = [1, 2];
// let secound = [3, 4];
// let bothPlus = [0, ...first, ...secound, 5]; //这会令bothPlus的值为[0, 1, 2, 3, 4, 5]。 展开操作创建了 first和second的一份浅拷贝。 它们不会被展开操作所改变。
// 5.4.2 展开对象  对象的展开比数组的展开要复杂的多。 像数组展开一样，它是从左至右进行处理，但结果仍为对象
// let defaults = { food: 'spicy', price: '$$', ambiance: 'noisy' };
// let search = { ...defaults, food: 'rich' }; //输出结果{ food: 'rich', price: '$$', ambiance: 'noisy' }
// let search1 = { food: 'rich', ...defaults };
// 对象展开有一些意想不到的限制:展开一个对象实例时，你会丢失其方法。例如：
// class C {
//   p = 12;
//   m() {}
// }
// let c = new C();
// let clone = { ...c };
// clone.p;
// clone.m; //编辑器提示：类型“{ p: number; }”上不存在属性“m”。
